import base64
import pickle

from django.shortcuts import render
from django_redis import get_redis_connection
from rest_framework import status
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView

from cart import constants
from cart.serializers import CartSerializer, CartSKUSerializer, CartDelSerializer, CartSelectAllSerializer
from goods.models import Goods


# Create your views here.


# PUT /cart/selection/
# class CartSelectAllView(APIView):
#     permission_classes = [IsAuthenticated]
#
#     # def perform_authentication(self, request):
#     #     """让当前视图跳过DRF框架认证过程"""
#     #     # pass
#
#     def put(self, request):
#         """
#         购物车记录全选和取消全选:
#         1. 获取参数并进行校验(selected必传)
#         2. 设置用户购物车记录勾选状态
#             2.1 如果用户已登录，设置redis中购物车记录勾选状态
#             2.2 如果用户未登录，设置cookie中购物车记录勾选状态
#         3. 返回应答，操作成功
#         """
#         # 1. 获取参数并进行校验(selected必传)
#         serializer = CartSelectAllSerializer(data=request.data)
#         serializer.is_valid(raise_exception=True)
#
#         # 获取校验之后的selected
#         selected = serializer.validated_data['selected']  # True: 全选 False: 全不选
#
#         # 获取user
#         try:
#             # 此代码会触发DRF框架的认证过程，在客户端传递的JWT token无效，会产生错误
#             user = request.user
#         except Exception:
#             user = None
#
#         # 2. 设置用户购物车记录勾选状态
#         if user and user.is_authenticated:
#             # 2.1 如果用户已登录，设置redis中购物车记录勾选状态
#             redis_conn = get_redis_connection('cart')
#
#             # 从redis hash元素中获取用户购物车所有商品的id
#             cart_key = 'cart_%s' % user.id
#
#             sku_ids = redis_conn.hkeys(cart_key)  # list
#
#             cart_selected_key = 'cart_selected_%s' % user.id
#
#             if selected:
#                 # 全选：将用户购物车中所有商品的id添加redis set元素中
#                 redis_conn.sadd(cart_selected_key, *sku_ids)
#             else:
#                 # 全不选：将用户购物车中所有商品的id从redis set元素中移除
#                 redis_conn.srem(cart_selected_key, *sku_ids)
#
#             return Response({'message': 'OK'})
#         else:
#             response = Response({'message': 'OK'})
#             # 2.2 如果用户未登录，设置cookie中购物车记录勾选状态
#             cookie_cart = request.COOKIES.get('cart')  # None
#
#             if cookie_cart is None:
#                 # 购物车无数据
#                 return response
#
#             # 解析cookie中购物车数据
#             # {
#             #     '<sku_id>': {
#             #         'count': '<count>',
#             #         'selected': '<selected>'
#             #     },
#             #     ...
#             # }
#             cart_dict = pickle.loads(base64.b64decode(cookie_cart))  # {}
#
#             if not cart_dict:
#                 # 字典为空，购物车无数据
#                 return response
#
#             # 设置cookie购物车记录勾选状态
#             for sku_id in cart_dict:
#                 cart_dict[sku_id]['selected'] = selected
#
#             # 3. 返回应答，操作成功
#             # 设置cookie购物车数据
#             cart_data = base64.b64encode(pickle.dumps(cart_dict)).decode()  # str
#             response.set_cookie('cart', cart_data, max_age=constants.CART_COOKIE_EXPIRES)
#             return response


# POST /cart/
class CartView(APIView):
    permission_classes = [IsAuthenticated]

    def delete(self, request):
        """
        购物车记录删除:
        1. 获取sku_id并进行校验(参数完整性，sku_id对应商品是否存在)
        2. 删除用户的购物车记录
            2.1 如果用户已登录，删除redis中用户对应的购物车记录
            2.2 如果用户未登录，删除cookie中用户对应的购物车记录
        3. 返回应答，删除成功
        """
        # 1. 获取sku_id并进行校验(参数完整性，sku_id对应商品是否存在)
        serializer = CartDelSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        # 获取校验之后数据
        goods_id = serializer.validated_data['goods_id']

        # 获取user

        # 此代码会触发DRF框架的认证过程，在客户端传递的JWT token无效，会产生错误
        user = request.user

        # 2. 删除用户的购物车记录

        # 2.1 如果用户已登录，删除redis中用户对应的购物车记录
        redis_conn = get_redis_connection('cart')

        # 从redis对应hash元素中删除对应商品的id和数量count
        cart_key = 'cart_%s' % user.id
        redis_conn.hdel(cart_key, goods_id)
        total_count = redis_conn.hlen(cart_key)

        # 从redis对应set元素中删除对应商品的勾选状态
        cart_selected_key = 'cart_selected_%s' % user.id
        redis_conn.srem(cart_selected_key, goods_id)

        # 返回应答
        return Response({'total_count': total_count}, status=status.HTTP_204_NO_CONTENT)

    def put(self, request):
        """
        购物车记录修改:
        1. 获取参数并进行校验(参数完整性，商品是否存在，商品库存是否足够)
        2. 修改用户的购物车记录
            2.1 如果用户已登录，修改用户redis中的购物车中记录
            2.2 如果用户未登录，修改用户cookie中的购物车记录
        3. 返回应答，修改成功
        """
        # 1. 获取参数并进行校验(参数完整性，商品是否存在，商品库存是否足够)
        serializer = CartSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        # 获取校验之后的数据
        goods_id = serializer.validated_data['goods_id']
        count = serializer.validated_data['count']  # 修改数量
        selected = serializer.validated_data['selected']  # 修改勾选状态

        # 获取user

        user = request.user

        # 2. 修改用户的购物车记录

        # 2.1 获取用户，修改用户redis中的购物车中记录
        redis_conn = get_redis_connection('cart')

        # 修改redis对应hash元素中指定商品的数量count
        cart_key = 'cart_%s' % user.id
        redis_conn.hset(cart_key, goods_id, count)

        # 修改redis对应set元素中购物车商品勾选状态
        cart_selected_key = 'cart_selected_%s' % user.id

        total_count = redis_conn.hlen(cart_key)

        if selected:
            # 勾选，将sku_id添加到对应的set元素中
            redis_conn.sadd(cart_selected_key, goods_id)
        else:
            # 取消勾选，将sku_id从对应的set元素中移除
            redis_conn.srem(cart_selected_key, goods_id)

        # 返回应答
        # return Response(serializer.data)
        return Response({'total_count': total_count})

    def get(self, request):
        """
        购物车记录获取:
        1. 获取user
        2. 获取用户的购物车记录
            2.1 如果用户已登录，从redis中获取用户的购物车记录
            2.2 如果用户未登录，从cookie中获取用户的购物车记录
        3. 根据用户购物车中商品的sku_id获取对应商品的数据
        4. 将用户购物车中商品的数据序列化并返回
        """
        # 1. 获取user

        user = request.user

        # 2. 获取用户的购物车记录

        # 2.1 如果用户已登录，从redis中获取用户的购物车记录
        redis_conn = get_redis_connection('cart')

        # 从redis的对应hash元素中获取用户购物车中商品的id和对应数量count
        cart_key = 'cart_%s' % user.id
        # {
        #     b'<sku_id>': b'<count>',
        #     ...
        # }
        cart_redis = redis_conn.hgetall(cart_key)

        # 从redis的对应set元素中获取用户购物车中被勾选商品的id
        cart_selected_key = 'cart_selected_%s' % user.id
        # # (b'<sku_id>', b'<sku_id>, ...)
        cart_selected_redis = redis_conn.smembers(cart_selected_key)

        # 组织数据
        # {
        #     '<sku_id>': {
        #         'count': '<count>',
        #         'selected': '<selected>'
        #     },
        #     ...
        # }
        cart_dict = {}

        for goods_id, count in cart_redis.items():
            cart_dict[int(goods_id)] = {
                'count': int(count),
                'selected': goods_id in cart_selected_redis
            }

            # 3. 根据用户购物车中商品的sku_id获取对应商品的数据
        goods_ids = cart_dict.keys()
        goods = Goods.objects.filter(id__in=goods_ids)

        for good in goods:
            # 给sku对象增加属性count和selected
            # 分别保存该商品在用户的购物车中添加数量和勾选状态
            good.count = cart_dict[good.id]['count']
            good.selected = cart_dict[good.id]['selected']

        # 4. 将用户购物车中商品的数据序列化并返回
        serializer = CartSKUSerializer(goods, many=True)
        return Response(serializer.data)

    def post(self, request):
        """
        购物车记录的添加:
        1. 接收参数并进行校验(参数完整性，商品是否存在，商品库存是否足够)
        2. 保存用户的购物车记录
            2.1 如果用户已登录，在redis中保存用户的购物车记录
            2.2 如果用户未登录，在cookie中保存用户的购物车记录
        3. 返回应答，购物车记录保存成功
        """
        # 1. 接收参数并进行校验(参数完整性，商品是否存在，商品库存是否足够)
        serializer = CartSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        # 获取校验之后的数据
        goods_id = serializer.validated_data['goods_id']
        count = serializer.validated_data['count']
        selected = serializer.validated_data['selected']

        # 获取user

        # 此代码会触发DRF框架的认证过程，在客户端传递的JWT token无效，会产生错误
        user = request.user

        # 2.1 用户，在redis中保存用户的购物车记录
        redis_conn = get_redis_connection('cart')

        # 在redis对应hash元素中存储用户购物车添加商品的id和对应数量count
        cart_key = 'cart_%s' % user.id

        # 如果用户购物车中已经添加过该商品，那么商品的数量需要进行累加
        redis_conn.hincrby(cart_key, goods_id, count)

        total_count = redis_conn.hlen(cart_key)

        # 在redis对应set元素中存储用户购物车被勾选的商品的id
        cart_selected_key = 'cart_selected_%s' % user.id
        if selected:
            redis_conn.sadd(cart_selected_key, goods_id)

        return Response({'total_count': total_count})

# 购物车商品条数
class CartCount(APIView):
    permission_classes = [IsAuthenticated]

    def get(self, request):
        user = request.user
        redis_conn = get_redis_connection('cart')
        cart_key = 'cart_%s' % user.id
        total_count = redis_conn.hlen(cart_key)
        return Response({'total_count': total_count})
